package com.ztom.context.support

import com.ztom.beans.factory.ConfigurableListableBeanFactory
import com.ztom.beans.factory.support.processor.BeanFactoryPostProcessor
import com.ztom.beans.factory.support.processor.BeanPostProcessor
import com.ztom.context.ConfigurableApplicationContext
import com.ztom.context.support.aware.ApplicationContextAwareProcessor
import com.ztom.context.support.event.event.ApplicationEvent
import com.ztom.context.support.event.event.ContextClosedEvent
import com.ztom.context.support.event.event.ContextRefreshedEvent
import com.ztom.context.support.event.listener.ApplicationListener
import com.ztom.context.support.event.multicast.ApplicationEventMulticaster
import com.ztom.context.support.event.multicast.SimpleApplicationEventMulticaster
import com.ztom.core.io.DefaultResourceLoader
import java.beans.Introspector
import kotlin.reflect.KClass

/**
 * @author ZhangTao
 */
abstract class AbstractApplicationContext : DefaultResourceLoader(), ConfigurableApplicationContext {

    private var applicationEventMulticaster: ApplicationEventMulticaster? = null

    override fun refresh() {
        // 获取 BeanFactory, 加载 xml 配置文件中的 bean 定义信息
        val beanFactory: ConfigurableListableBeanFactory = obtainFreshBeanFactory()
        // 执行 BeanFactoryPostProcessor
        invokeBeanFactoryPostProcessors(beanFactory)
        // 注册 BeanPostProcessor
        registerBeanPostProcessors(beanFactory)
        // 初始化事件广播器
        initApplicationEventMulticaster()
        // 注册监听器
        registerListeners()
        // 提前初始化所有单例 bean
        beanFactory.preInstantiateSingletons()
        // 完成容器刷新工作
        finishRefresh()
    }

    private fun obtainFreshBeanFactory(): ConfigurableListableBeanFactory {
        refreshBeanFactory()
        return getBeanFactory()
    }

    protected abstract fun refreshBeanFactory()

    protected abstract fun getBeanFactory(): ConfigurableListableBeanFactory

    private fun invokeBeanFactoryPostProcessors(beanFactory: ConfigurableListableBeanFactory) {
        beanFactory.getBeansOfType(BeanFactoryPostProcessor::class)
            .values.forEach { it.postProcessBeanFactory(beanFactory) }
    }

    private fun registerBeanPostProcessors(beanFactory: ConfigurableListableBeanFactory) {
        // 注册 ApplicationContextAwarePostProcessor
        beanFactory.addBeanPostProcessor(ApplicationContextAwareProcessor(this))

        beanFactory.getBeansOfType(BeanPostProcessor::class)
            .values.forEach { beanFactory.addBeanPostProcessor(it) }
    }

    private fun initApplicationEventMulticaster() {
        val beanFactory = getBeanFactory()
        this.applicationEventMulticaster = SimpleApplicationEventMulticaster(beanFactory)
        // 放入容器
        beanFactory.addSingleton(
            Introspector.decapitalize(ApplicationEventMulticaster::class.simpleName),
            applicationEventMulticaster as SimpleApplicationEventMulticaster
        )
    }

    private fun registerListeners() {
        getBeanFactory().getBeansOfType(ApplicationListener::class)
            .values.forEach { applicationEventMulticaster!!.addApplicationListener(it) }
    }

    private fun finishRefresh() {
        // 完成刷新, 发布事件
        publishEvent(ContextRefreshedEvent(this))
    }

    override fun publishEvent(event: ApplicationEvent) {
        applicationEventMulticaster!!.multicastEvent(event)
    }

    override fun getBean(beanName: String): Any {
        return getBeanFactory().getBean(beanName)
    }

    override fun <T : Any> getBeansOfType(type: KClass<out T>): Map<String, T> {
        return getBeanFactory().getBeansOfType(type)
    }

    override fun getBeanDefinitionNames(): Array<String> {
        return getBeanFactory().getBeanDefinitionNames()
    }

    override fun close() {
        doClose()
    }

    private fun doClose() {
        // 即将关闭, 发布事件
        publishEvent(ContextClosedEvent(this))

        destroyBeans()
    }

    private fun destroyBeans() {
        getBeanFactory().destroySingletons()
    }

    override fun registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(Thread(::doClose))
    }
}